package android.media;

/**
 * MediaMetadataRetriever, 
 * porting from Android SDK 2.2, to get thumbnail for video in SDK 2.1 and lower.
 * <p>
 */
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;


public class MediaMetadataRetriever {
	static {
		if (Integer.valueOf(android.os.Build.VERSION.SDK) < 8) {
			System.loadLibrary("media_jni");
			native_init();
		}
	}

	// The field below is accessed by native methods
	@SuppressWarnings("unused")
	private int mNativeContext;

	public MediaMetadataRetriever() {
		native_setup();
	}

	/**
	 * Call this method before setDataSource() so that the mode becomes
	 * effective for subsequent operations. This method can be called only once
	 * at the beginning if the intended mode of operation for a
	 * MediaMetadataRetriever object remains the same for its whole lifetime,
	 * and thus it is unnecessary to call this method each time setDataSource()
	 * is called. If this is not never called (which is allowed), by default the
	 * intended mode of operation is to both capture frame and retrieve meta
	 * data (i.e., MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY). Often,
	 * this may not be what one wants, since doing this has negative performance
	 * impact on execution time of a call to setDataSource(), since both types
	 * of operations may be time consuming.
	 * 
	 * @param mode
	 *            The intended mode of operation. Can be any combination of
	 *            MODE_GET_METADATA_ONLY and MODE_CAPTURE_FRAME_ONLY: 1.
	 *            MODE_GET_METADATA_ONLY & MODE_CAPTURE_FRAME_ONLY: For neither
	 *            frame capture nor meta data retrieval 2.
	 *            MODE_GET_METADATA_ONLY: For meta data retrieval only 3.
	 *            MODE_CAPTURE_FRAME_ONLY: For frame capture only 4.
	 *            MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY: For both
	 *            frame capture and meta data retrieval
	 */
	public native void setMode(int mode);

	/**
	 * @return the current mode of operation. A negative return value indicates
	 *         some runtime error has occurred.
	 */
	public native int getMode();

	/**
	 * Sets the data source (file pathname) to use. Call this method before the
	 * rest of the methods in this class. This method may be time-consuming.
	 * 
	 * @param path
	 *            The path of the input media file.
	 * @throws IllegalArgumentException
	 *             If the path is invalid.
	 */
	public native void setDataSource(String path)
			throws IllegalArgumentException;

	/**
	 * Sets the data source (FileDescriptor) to use. It is the caller's
	 * responsibility to close the file descriptor. It is safe to do so as soon
	 * as this call returns. Call this method before the rest of the methods in
	 * this class. This method may be time-consuming.
	 * 
	 * @param fd
	 *            the FileDescriptor for the file you want to play
	 * @param offset
	 *            the offset into the file where the data to be played starts,
	 *            in bytes. It must be non-negative
	 * @param length
	 *            the length in bytes of the data to be played. It must be
	 *            non-negative.
	 * @throws IllegalArgumentException
	 *             if the arguments are invalid
	 */
	public native void setDataSource(FileDescriptor fd, long offset, long length)
			throws IllegalArgumentException;

	/**
	 * Sets the data source (FileDescriptor) to use. It is the caller's
	 * responsibility to close the file descriptor. It is safe to do so as soon
	 * as this call returns. Call this method before the rest of the methods in
	 * this class. This method may be time-consuming.
	 * 
	 * @param fd
	 *            the FileDescriptor for the file you want to play
	 * @throws IllegalArgumentException
	 *             if the FileDescriptor is invalid
	 */
	public void setDataSource(FileDescriptor fd)
			throws IllegalArgumentException {
		// intentionally less than LONG_MAX
		setDataSource(fd, 0, 0x7ffffffffffffffL);
	}

	/**
	 * Sets the data source as a content Uri. Call this method before the rest
	 * of the methods in this class. This method may be time-consuming.
	 * 
	 * @param context
	 *            the Context to use when resolving the Uri
	 * @param uri
	 *            the Content URI of the data you want to play
	 * @throws IllegalArgumentException
	 *             if the Uri is invalid
	 * @throws SecurityException
	 *             if the Uri cannot be used due to lack of permission.
	 */
	public void setDataSource(Context context, Uri uri)
			throws IllegalArgumentException, SecurityException {
		if (uri == null) {
			throw new IllegalArgumentException();
		}

		String scheme = uri.getScheme();
		if (scheme == null || scheme.equals("file")) { // 匹配文件
			setDataSource(uri.getPath());
			return;
		}

		AssetFileDescriptor fd = null;
		try {
			ContentResolver resolver = context.getContentResolver();
			try {
				fd = resolver.openAssetFileDescriptor(uri, "r");// 读取文件
			} catch (FileNotFoundException e) {
				throw new IllegalArgumentException();
			}
			if (fd == null) {
				throw new IllegalArgumentException();
			}
			FileDescriptor descriptor = fd.getFileDescriptor();
			if (!descriptor.valid()) {
				throw new IllegalArgumentException();
			}
			// Note: using getDeclaredLength so that our behavior is the same
			// as previous versions when the content provider is returning
			// a full file.
			if (fd.getDeclaredLength() < 0) {
				setDataSource(descriptor);
			} else {
				setDataSource(descriptor, fd.getStartOffset(),
						fd.getDeclaredLength());
			}
			return;
		} catch (SecurityException ex) { 
		} finally {
			try {
				if (fd != null) {
					fd.close();
				}
			} catch (IOException ioEx) {
			}
		}
		setDataSource(uri.toString());
	}

	/**
	 * Call this method after setDataSource(). This method retrieves the meta
	 * data value associated with the keyCode.
	 * 
	 * The keyCode currently supported is listed below as METADATA_XXX
	 * constants. With any other value, it returns a null pointer.
	 * 
	 * @param keyCode
	 *            One of the constants listed below at the end of the class.
	 * @return The meta data value associate with the given keyCode on success;
	 *         null on failure.
	 */
	public native String extractMetadata(int keyCode);

	/**
	 * Call this method after setDataSource(). This method finds a
	 * representative frame if successful and returns it as a bitmap. This is
	 * useful for generating a thumbnail for an input media source.
	 * 
	 * @return A Bitmap containing a representative video frame, which can be
	 *         null, if such a frame cannot be retrieved.
	 */
	public native Bitmap captureFrame();

	/**
	 * Call this method after setDataSource(). This method finds the optional
	 * graphic or album art associated (embedded or external url linked) the
	 * related data source.
	 * 
	 * @return null if no such graphic is found.
	 */
	public native byte[] extractAlbumArt();

	/**
	 * Call it when one is done with the object. This method releases the memory
	 * allocated internally.
	 */
	public native void release();

	private native void native_setup();

	private static native void native_init();

	private native final void native_finalize();

	@Override
	protected void finalize() throws Throwable {
		try {
			native_finalize();
		} finally {
			super.finalize();
		}
	}

	public static final int MODE_GET_METADATA_ONLY = 0x01;

	public static final int MODE_CAPTURE_FRAME_ONLY = 0x02;

	/*
	 * Do not change these values without updating their counterparts in
	 * include/media/mediametadataretriever.h!
	 */
	public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;

	public static final int METADATA_KEY_ALBUM = 1;

	public static final int METADATA_KEY_ARTIST = 2;

	public static final int METADATA_KEY_AUTHOR = 3;

	public static final int METADATA_KEY_COMPOSER = 4;

	public static final int METADATA_KEY_DATE = 5;

	public static final int METADATA_KEY_GENRE = 6;

	public static final int METADATA_KEY_TITLE = 7;

	public static final int METADATA_KEY_YEAR = 8;

	public static final int METADATA_KEY_DURATION = 9;

	public static final int METADATA_KEY_NUM_TRACKS = 10;

	public static final int METADATA_KEY_IS_DRM_CRIPPLED = 11;

	public static final int METADATA_KEY_CODEC = 12;

	public static final int METADATA_KEY_RATING = 13;

	public static final int METADATA_KEY_COMMENT = 14;

	public static final int METADATA_KEY_COPYRIGHT = 15;

	public static final int METADATA_KEY_BIT_RATE = 16;

	public static final int METADATA_KEY_FRAME_RATE = 17;

	public static final int METADATA_KEY_VIDEO_FORMAT = 18;

	public static final int METADATA_KEY_VIDEO_HEIGHT = 19;

	public static final int METADATA_KEY_VIDEO_WIDTH = 20;

	public static final int METADATA_KEY_WRITER = 21;

	public static final int METADATA_KEY_MIMETYPE = 22;

	public static final int METADATA_KEY_DISCNUMBER = 23;

	public static final int METADATA_KEY_ALBUMARTIST = 24;
	// Add more here...
}